/*
HTTP请求处理器
负责处理压力测试相关的HTTP API请求
主要包括：
1. 配置重载接口
2. 压力测试启动接口
3. 压力测试停止接口
4. 任务回调处理
*/
package service

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"sync"
	"time"

	"github.com/bwmarrin/snowflake"
	"github.com/levigross/grequests"
	"github.com/valyala/fasthttp"
	"gitlab.vrviu.com/inviu/backend-go-public/gopublic"
	"gitlab.vrviu.com/inviu/backend-go-public/gopublic/config"
	"gitlab.vrviu.com/inviu/backend-go-public/gopublic/rediswrap"
	"gitlab.vrviu.com/inviu/backend-go-public/gopublic/vlog"
)

// 压力测试模式常量定义
const (
	InstantPressureMode = 1 // 瞬时发压模式 - 按QPS均匀分布请求
	NormalPressureMode  = 2 // 普通发压模式 - 并发启动所有客户端
)

// ReloadConfig 重载配置文件
// 用于运行时动态更新配置，无需重启服务
// 返回配置差异信息
func ReloadConfig(*fasthttp.RequestCtx) (errCode int, errMsg string, rspBody interface{}) {
	//filename := string(ctx.QueryArgs().Peek("filename"))
	testFileName := "./conf/app.conf"

	// 重载指定的配置文件
	//diff, err := config.GetConfigManager().Reload(filename)
	diff, err := config.GetConfigManager().Reload(testFileName)
	//vlog.Infof("config filename %s.", testFileName)

	if err != nil {
		return -1, err.Error(), nil
	}

	return 0, "ok", diff
}

// CallBack 通知任务完成回调
// 向主控服务器发送任务完成通知
// 参数：
//
//	masterHost: 主控服务器地址
//	taskID: 任务ID
func CallBack(masterHost, taskID string) error {
	// 构建回调请求数据
	reqData := map[string]interface{}{
		"task_id": taskID,
	}

	// 发送POST请求到主控服务器
	resp, err := grequests.Post(
		"http://"+masterHost+"/v1/task/notify",
		&grequests.RequestOptions{JSON: reqData},
	)
	if err != nil {
		return err
	}

	// 解析回调响应
	var result CallbackResult
	if err := resp.JSON(&result); err != nil {
		vlog.Errorf("failed to parse callback JSON error(%v)", err)
	}
	vlog.Infof("callback result (%v)", result)
	return nil
}

// newRoutineController 创建新的协程控制器
// 用于管理和控制压力测试任务的生命周期
func newRoutineController() *routine_controller {
	ctx, cancel := context.WithCancel(context.Background())
	return &routine_controller{
		ctx:    ctx,
		cancel: cancel,
	}
}

// initClient 初始化客户端
// 根据请求参数创建并启动指定数量的虚拟客户端
// 参数：
//
//	wg: 同步等待组
//	body: 压力测试参数
//	clientController: 客户端控制器
func initClient(wg *sync.WaitGroup, body StressTestBody, clientController *ClientController) {
	// 从配置文件读取环境相关的服务地址
	tokenHost := config.GetConfigManager().Section("env").Key(fmt.Sprintf("%s_token_host", body.Env)).MustString("api-bnd.yuntiancloud.com:18088")
	accessHost := config.GetConfigManager().Section("env").Key(fmt.Sprintf("%s_access_host", body.Env)).MustString("union-access.yuntiancloud.com:18108")
	areaType := config.GetConfigManager().Section("service").Key("area_type").MustInt(0)
	relaunchDuration := config.GetConfigManager().Section("service").Key("relaunch_duration").MustInt(5)

	var step uint64 = 1
	// 循环创建指定数量的客户端
	for step <= body.Total {
		wg.Add(1)
		vlog.Infof("user:[%s] wg add!!!", body.StartUUID)

		// 创建客户端管理器
		//acm := CreateAreaClientMgr(body.UserID, body.StartUUID, body.GID, body.Biz, tokenHost, accessHost, body.TaskID, 1, areaType, int(body.HoldTime), relaunchDuration, wg, clientController)

		clientMgr := &AreaClientMgr{
			AreaClients: make([]*AreaClient, 1),
			Result_key:  fmt.Sprintf("%s_performance_temp_flow", body.TaskID),
		}

		// 创建单个客户端实例
		for i := 0; i < 1; i++ {
			var newUserId string
			if clientController.IsAutoResist == false {
				// 使用递增ID生成用户ID
				newId := body.StartUUID + uint64(i)
				newUserId = strconv.FormatUint(newId, 10)
			} else {
				// 使用雪花算法生成唯一用户ID
				node, _ := snowflake.NewNode(1) // Node ID 范围 0-1023，需唯一
				snowflakeID := node.Generate()
				newUserId = fmt.Sprintf("%s_%s", body.UserID, snowflakeID.String())
				gopublic.GenerateRandonString(12)
			}

			// 创建AreaClient实例
			clientMgr.AreaClients[i] = CreateAreaClient(0, body.GID, body.Biz, newUserId, tokenHost, accessHost, clientMgr.Result_key, areaType, body.HoldTime, relaunchDuration, rediswrap.GetRedisCli(), wg, clientController)
		}

		// 根据性能模式选择不同的发压策略
		switch body.PerformanceMode {
		case InstantPressureMode:
			// 瞬时发压模式：按指定QPS均匀分布发送请求
			clientMgr.instantPressure(int(body.SPConcurrency))
		case NormalPressureMode:
			// 普通发压模式：并发启动所有客户端
			clientMgr.normalPressure()
		default:
			vlog.Warnf("Unsupported performance mode [%d] for user [%s], UUID [%d]", body.PerformanceMode, body.UserID, body.StartUUID)
		}
		body.StartUUID += 1
		step += 1
	}
}

// Start 开始压力测试
// HTTP API端点：POST /v1/aclient/start
// 解析请求参数，创建客户端，启动压力测试任务
func Start(ctx *fasthttp.RequestCtx) (errCode int, errMsg string, rspBody interface{}) {
	// 解析请求体中的压力测试参数
	var body StressTestBody
	if err := json.Unmarshal(ctx.Request.Body(), &body); err != nil {
		return 1000, err.Error(), rspBody
	}

	// 重置CSV管理器，为新的压测任务创建新的CSV文件
	ResetCSVManager()
	vlog.Infof("CSV manager reset for new stress test task: %s", body.TaskID)

	// 获取主控服务器地址（用于任务完成回调）
	masterHost := config.GetConfigManager().Section("service").Key("master_host").MustString("")

	// 创建客户端控制器，设置测试参数
	clientController := &ClientController{
		IsStreaming:  body.IsStreaming,
		IsQuickOut:   body.IsQuickOut,
		IsAutoResist: body.IsAutoRegister,
		DelayExit:    body.DelayExit,
		RsetID:       body.RSetID,
		TaskID:       body.TaskID,
		IsTrylock:    body.IsTrylock,
	}
	wg := &sync.WaitGroup{}

	// 创建任务控制器，用于管理任务生命周期
	taskController := newRoutineController()

	// 将任务控制器存储到全局map中，便于后续停止操作
	ctx_map.Store(body.TaskID, taskController)
	vlog.Infof("total is %v", body.Total)

	// 启动客户端初始化协程
	go initClient(wg, body, clientController)

	// 启动任务监控协程
	go func() {
		// 等待2秒后开始监控任务状态
		time.Sleep(2 * time.Second)
		// 等待所有客户端完成
		wg.Wait()
		// 取消任务上下文
		taskController.cancel()
		vlog.Infof("task:%s finish complete!!!", body.TaskID)

		// 关闭CSV文件，确保数据完整写入
		if csvManager := GetCSVManager(); csvManager != nil {
			csvManager.Close()
			vlog.Infof("CSV file closed for task: %s", body.TaskID)
		}

		// 清理Redis数据（如果配置了清理选项）
		key := fmt.Sprintf("%s_performance_temp_flow", body.TaskID)
		if body.IsDelKey {
			rdsCli := rediswrap.GetRedisCli()
			rdsCli.Del(ctx, key)
			vlog.Infof("clean up redis information end，key is %v", key)
		}

		// 发送任务完成回调
		if err := CallBack(masterHost, body.TaskID); err != nil {
			vlog.Errorf("callback error")
		}
		return
	}()

	return 0, "ok", nil
}

// Stop 停止压力测试
// HTTP API端点：GET /v1/server/stop?task_id=xxx
// 根据任务ID停止正在运行的压力测试任务
func Stop(ctx *fasthttp.RequestCtx) (errCode int, errMsg string, rspBody interface{}) {
	// 从查询参数获取任务ID
	taskId := string(ctx.QueryArgs().Peek("task_id"))

	// 从全局map中查找对应的任务控制器
	if value, ok := ctx_map.Load(taskId); ok {
		if controller, ok := value.(*routine_controller); ok {
			// 调用取消函数停止任务
			controller.cancel()
		} else {
			vlog.Errorf("Cancel task fail!!")
		}
	} else {
		vlog.Errorf("no ctx found in sync.map!!!!!!!")
	}
	return 0, "ok", nil
}
